package com.zxjbyte.yiyi.framework.common.core.okhttp;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.zxjbyte.yiyi.framework.common.exception.ServiceException;
import com.zxjbyte.yiyi.framework.common.util.JsonUtil;
import com.zxjbyte.yiyi.framework.common.util.ServletXUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Headers;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.internal.Util;
import okio.BufferedSink;
import okio.Okio;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * okHttp 响应封装
 * @Author zhangxingjia
 * @Date 2023/4/24 10:55
 * @Version: 1.0
 */
@Slf4j
public class OkResponse implements IResponseProcessor {

    private final Request request;
    private final Response response;
    private final ResponseBody responseBody;

    public OkResponse(final Response response) {
        this.request = response.request();
        this.response = response;
        this.responseBody = bodyNullToEmpty(response.body());
    }

    public static OkResponse of(final Response response){
        return new OkResponse(response);
    }

    @Override
    public Request origRequest() {
        return this.request;
    }

    @Override
    public Response origResponse() {
        return this.response;
    }

    @Override
    public ResponseBody origResponseBody() {
        return this.responseBody;
    }

    @Override
    public int getStatus() {
        return response.code();
    }

    /**
     *  is success
     * @return
     */
    public boolean isOk(){
        return response.isSuccessful();
    }

    @Override
    public Headers responseHeaders() {
        return response.headers();
    }

    /**
     * Returns body String.
     * @return
     */
    public String deseStr(){
        return handleResponse(String.class);
    }

    /**
     * Returns body to byte arrays.
     * @return
     */
    public byte[] deseBytes(){
        return handleResponse(byte[].class);
    }

    /**
     * Returns body to File.
     * @return
     */
    public File deseFile(){
        return handleResponse(File.class);
    }

    public File deseFile(String path){
        return handleResponse(File.class, path);
    }

    /**
     * Returns body to InputStream.
     * @return
     */
    public InputStream deseStream(){
        return handleResponse(InputStream.class);
    }

    /**
     * 反序列化
     * @param returnType 指定类型
     * @param <T>
     * @return
     */
    public <T> T deserialize(Type returnType){
        return handleResponse(returnType);
    }

    private ResponseBody bodyNullToEmpty(ResponseBody body) {
        return body == null ? Util.EMPTY_RESPONSE : body;
    }

    @SneakyThrows
    private <T> T handleResponse(Type returnType, Object... param) {
        if (returnType == null || response.code() == 204) {
            // 如果未定义returnType，或者状态代码为204（无内容），则返回null
            responseBody.close();
            return null;
        } else if ("byte[]".equals(returnType.toString())) {
            // 处理二进制响应（字节数组）
            return (T) responseBody.bytes();
        } else if (returnType.equals(File.class)) {
            // 处理文件下载
            return (T) downloadFileFromResponse(param);
        } else if (returnType.equals(InputStream.class)){
            return (T) responseBody.byteStream();
        }
        String contentType = response.headers().get("Content-Type");
        if(StrUtil.isBlank(contentType)){
            contentType = "application/json";
        }
        String respBody = responseBody.string();
        if (returnType.equals(String.class)) {
            return (T) respBody;
        } else if(ServletXUtil.isJsonMime(contentType)){
            if(returnType.equals(JsonNode.class)){
                return (T) JsonUtil.readTree(respBody);
            } else {
                return JsonUtil.parseObject(respBody, returnType);
            }
        } else if("text/plain".equals(contentType)){
            return JsonUtil.parseObject(respBody, returnType);
        }
        throw new ServiceException("Content type " + contentType + " is not supported for type: " + returnType);
    }

    private File downloadFileFromResponse(Object[] param) throws IOException {
        File file = prepareDownloadFile(param);
        BufferedSink sink = Okio.buffer(Okio.sink(file));
        // 将响应体中的数据写入文件
        sink.writeAll(responseBody.source());
        sink.close();
        return file;
    }

    private File prepareDownloadFile(Object[] param) {
        if(param != null && param.length >= 1 && param[0] instanceof String){
            return FileUtil.file((String)param[0]);
        }
        String filename = null;
        String contentDisposition = response.header("Content-Disposition");
        if (StrUtil.isNotBlank(contentDisposition)) {
            // Get filename from the Content-Disposition header.
            Pattern pattern = Pattern.compile("filename=['\"]?([^'\"\\s]+)['\"]?");
            Matcher matcher = pattern.matcher(contentDisposition);
            if (matcher.find()) {
                filename = matcher.group(1).replaceAll(".*[/\\\\]", "");
            }
        }
        String prefix = null;
        String suffix = null;
        if (filename == null) {
            prefix = "download-";
            suffix = "";
        } else {
            int pos = filename.lastIndexOf(".");
            if (pos == -1) {
                prefix = filename + "-";
            } else {
                prefix = filename.substring(0, pos) + "-";
                suffix = filename.substring(pos);
            }
            // Files.createTempFile requires the prefix to be at least three characters long
            if (prefix.length() < 3) {
                prefix = "download-";
            }
        }
        return FileUtil.createTempFile(prefix, suffix, true);
    }

    @Override
    public void close() throws IOException {
        Util.closeQuietly(this.responseBody);
    }
}
